package com.kexio.auth.satoken;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.kexio.auth.config.TenantModeProperties;
import com.kexio.auth.context.TenantContextHolder;
import com.kexio.auth.dto.RoleInfo;
import com.kexio.auth.dto.UserAuthInfo;
import com.kexio.auth.spi.UserAuthInfoProvider;

import cn.dev33.satoken.stp.StpInterface;

/**
 * Sa-Token权限接口实现
 * 
 * 集成Kexio现有权限体系，提供Sa-Token所需的权限和角色信息
 * 
 * 核心功能：
 * 1. 通过UserAuthInfoProvider获取用户权限信息
 * 2. 支持多租户权限隔离
 * 3. 集成现有缓存体系提升性能
 * 4. 提供优雅的异常处理和降级机制
 * 
 * 架构集成：
 * - 复用现有的UserAuthInfoProvider SPI
 * - 保持与TenantContextHolder的集成
 * - 利用现有的权限缓存机制
 * - 支持权限版本控制和缓存失效
 * 
 * @author Kexio Team
 * @since 1.0.0 - Sa-Token集成版本
 */
@Component("saTokenStpInterface")
public class SaTokenStpInterfaceImpl implements StpInterface {

    private static final Logger logger = LoggerFactory.getLogger(SaTokenStpInterfaceImpl.class);

    /**
     * ✅ 优化：请求级缓存，避免同一请求中重复加载用户权限信息
     * 
     * 背景：Sa-Token在一次权限检查中会先后调用 getPermissionList() 和 getRoleList()
     * 优化前：每次调用都会完整加载一次用户信息（包括数据库查询）
     * 优化后：使用ThreadLocal缓存，同一请求内只加载一次
     * 收益：减少50%的数据库查询
     */
    private static final ThreadLocal<Map<String, UserAuthInfo>> REQUEST_CACHE = 
        ThreadLocal.withInitial(HashMap::new);

    /**
     * 用户权限信息提供者
     * 
     * 通过SPI方式集成，支持多种实现：
     * - UserAuthInfoProviderImpl: 数据库实现（主要使用）
     * - 其他自定义实现
     */
    @Autowired(required = false)
    private UserAuthInfoProvider userAuthInfoProvider;
    
    /**
     * 租户模式配置
     */
    @Autowired(required = false)
    private TenantModeProperties tenantModeProperties;

    /**
     * 获取指定账号的权限列表
     * 
     * Sa-Token集成策略：
     * 1. 从当前租户上下文获取租户ID
     * 2. 通过UserAuthInfoProvider获取用户完整权限信息
     * 3. 提取权限集合并返回给Sa-Token
     * 4. 处理异常情况，确保认证流程稳定
     * 
     * @param loginId 登录ID（用户ID）
     * @param loginType 登录类型（预留扩展，当前未使用）
     * @return 用户权限代码列表
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        if (loginId == null) {
            logger.warn("loginId为null，返回空权限列表");
            return Collections.emptyList();
        }

        // 解析复合loginId（支持多租户权限缓存隔离）
        LoginIdParts loginIdParts = parseLoginId(loginId.toString());
        String userId = loginIdParts.userId;
        String tenantId = loginIdParts.tenantId;

        try {
            // 使用缓存加载用户信息，避免重复查询
            UserAuthInfo userAuthInfo = getCachedUserAuthInfo(tenantId, userId);
            
            if (userAuthInfo == null) {
                logger.warn("用户信息不存在或无效: userId={}, tenantId={}", userId, tenantId);
                return Collections.emptyList();
            }

            // 检查用户状态
            if (!userAuthInfo.isValid()) {
                logger.warn("用户状态无效: userId={}, status={}", userId, userAuthInfo.getStatus());
                return Collections.emptyList();
            }

            // 提取权限列表
            if (userAuthInfo.getPermissions() == null || userAuthInfo.getPermissions().isEmpty()) {
                logger.debug("用户没有权限: userId={}", userId);
                return Collections.emptyList();
            }

            return new ArrayList<>(userAuthInfo.getPermissions());

        } catch (Exception e) {
            logger.error("获取用户权限失败: userId={}, error={}", userId, e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    /**
     * 获取指定账号的角色列表
     * 
     * Sa-Token集成策略：
     * 1. 通过UserAuthInfoProvider获取用户角色信息
     * 2. 提取角色代码列表
     * 3. 过滤无效角色（已禁用的角色）
     * 4. 支持超级管理员角色识别
     * 
     * @param loginId 登录ID（用户ID）
     * @param loginType 登录类型（预留扩展，当前未使用）
     * @return 用户角色代码列表
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        if (loginId == null) {
            logger.warn("loginId为null，返回空角色列表");
            return Collections.emptyList();
        }

        // 解析复合loginId（支持多租户权限缓存隔离）
        LoginIdParts loginIdParts = parseLoginId(loginId.toString());
        String userId = loginIdParts.userId;
        String tenantId = loginIdParts.tenantId;

        try {
            // 使用缓存加载用户信息，避免重复查询
            UserAuthInfo userAuthInfo = getCachedUserAuthInfo(tenantId, userId);
            
            if (userAuthInfo == null) {
                logger.warn("用户信息不存在或无效: userId={}, tenantId={}", userId, tenantId);
                return Collections.emptyList();
            }

            // 检查用户状态
            if (!userAuthInfo.isValid()) {
                logger.warn("用户状态无效: userId={}, status={}", userId, userAuthInfo.getStatus());
                return Collections.emptyList();
            }

            // 提取角色列表
            List<RoleInfo> roles = userAuthInfo.getRoles();
            if (roles == null || roles.isEmpty()) {
                logger.debug("用户没有角色: userId={}", userId);
                return Collections.emptyList();
            }

            // 过滤有效角色并提取角色代码
            return roles.stream()
                .filter(role -> role != null && StringUtils.hasText(role.getRoleCode()))
                .filter(RoleInfo::isEnabled)
                .map(RoleInfo::getRoleCode)
                .collect(Collectors.toList());

        } catch (Exception e) {
            logger.error("获取用户角色失败: userId={}, error={}", userId, e.getMessage(), e);
            return Collections.emptyList();
        }
    }

    // ==================== 辅助方法 ====================

    /**
     * ✅ 优化：获取缓存的用户权限信息
     * 
     * 使用ThreadLocal缓存同一请求中的用户信息，避免重复数据库查询
     * 
     * @param tenantId 租户ID
     * @param userId 用户ID
     * @return 用户权限信息
     */
    private UserAuthInfo getCachedUserAuthInfo(String tenantId, String userId) {
        // 生成缓存key
        String cacheKey = (tenantId != null ? tenantId : "null") + ":" + userId;
        
        // 从ThreadLocal缓存中获取
        Map<String, UserAuthInfo> cache = REQUEST_CACHE.get();
        
        if (cache.containsKey(cacheKey)) {
            logger.debug("✅ 命中请求缓存: userId={}", userId);
            return cache.get(cacheKey);
        }
        
        // 缓存未命中，从数据库加载
        logger.debug("📥 缓存未命中，加载用户信息: userId={}", userId);
        
        if (userAuthInfoProvider == null) {
            logger.warn("UserAuthInfoProvider未配置");
            return null;
        }
        
        UserAuthInfo userAuthInfo = userAuthInfoProvider.loadUserAuthInfo(tenantId, userId);
        
        // 存入缓存（即使为null也缓存，避免重复查询不存在的用户）
        cache.put(cacheKey, userAuthInfo);
        
        return userAuthInfo;
    }
    
    /**
     * ✅ 优化：清理请求缓存
     * 
     * 在请求结束后调用，防止ThreadLocal内存泄漏
     * 由 RequestCacheCleanupFilter 调用
     */
    public static void clearRequestCache() {
        REQUEST_CACHE.remove();
    }

    /**
     * 获取当前租户ID
     * 
     * 集成现有的租户上下文管理：
     * 1. 优先从TenantContextHolder获取
     * 2. 降级到默认租户
     * 3. 记录租户上下文状态
     * 
     * @return 当前租户ID
     */
    private String getCurrentTenantId() {
        try {
            String tenantId = TenantContextHolder.getCurrentTenantId();
            if (StringUtils.hasText(tenantId)) {
                return tenantId;
            }
            
            // 降级到默认租户
            logger.debug("租户上下文为空，使用默认租户");
            return "default";
            
        } catch (Exception e) {
            logger.warn("获取租户上下文失败，使用默认租户: {}", e.getMessage());
            return "default";
        }
    }

    /**
     * 获取用户权限信息（供调试使用）
     * 
     * @param userId 用户ID
     * @return 用户权限信息摘要
     */
    public String getUserPermissionSummary(String userId) {
        if (!StringUtils.hasText(userId) || userAuthInfoProvider == null) {
            return "N/A";
        }

        try {
            String tenantId = getCurrentTenantId();
            UserAuthInfo userInfo = userAuthInfoProvider.loadUserAuthInfo(tenantId, userId);
            
            if (userInfo == null) {
                return "用户不存在";
            }

            int permissionCount = userInfo.getPermissions() != null ? userInfo.getPermissions().size() : 0;
            int roleCount = userInfo.getRoles() != null ? userInfo.getRoles().size() : 0;
            
            return String.format("权限:%d个, 角色:%d个, 超管:%s, 版本:%s", 
                permissionCount, roleCount, userInfo.isSuperAdmin(), userInfo.getPermVersion());
                
        } catch (Exception e) {
            return "获取失败: " + e.getMessage();
        }
    }

    /**
     * 检查Sa-Token集成状态
     * 
     * @return 集成状态信息
     */
    public String getIntegrationStatus() {
        StringBuilder status = new StringBuilder();
        status.append("Sa-Token集成状态: ");
        
        if (userAuthInfoProvider != null) {
            status.append("UserAuthInfoProvider=").append(userAuthInfoProvider.getClass().getSimpleName());
        } else {
            status.append("UserAuthInfoProvider=未配置");
        }
        
        status.append(", TenantContext=").append(getCurrentTenantId());
        
        return status.toString();
    }
    
    /**
     * 解析复合loginId
     * 
     * 支持格式：
     * - 多租户模式：tenantId:userId
     * - 单租户模式：userId
     * 
     * @param loginId 原始loginId
     * @return 解析后的登录ID部分
     */
    private LoginIdParts parseLoginId(String loginId) {
        if (!StringUtils.hasText(loginId)) {
            return new LoginIdParts(null, null);
        }
        
        // 检查是否为复合ID格式
        if (loginId.contains(":")) {
            String[] parts = loginId.split(":", 2);
            if (parts.length == 2) {
                String tenantId = parts[0];
                String userId = parts[1];
                
                logger.debug("解析复合loginId: tenantId={}, userId={}", tenantId, userId);
                return new LoginIdParts(tenantId, userId);
            }
        }
        
        // 单一用户ID格式：需要根据租户功能开关决定行为
        if (tenantModeProperties != null && tenantModeProperties.isEnabled()) {
            // 租户功能开启：使用fallback租户ID  
            String fallbackTenantId = getCurrentTenantId();
            logger.debug("租户功能已启用，解析单一loginId: userId={}, fallbackTenantId={}", loginId, fallbackTenantId);
            return new LoginIdParts(fallbackTenantId, loginId);
        } else {
            // 租户功能禁用：完全移除租户概念，使用null tenantId
            logger.debug("租户功能已禁用，解析单一loginId: userId={}, tenantId=null", loginId);
            return new LoginIdParts(null, loginId);
        }
    }
    
    /**
     * LoginId解析结果
     */
    private static class LoginIdParts {
        final String tenantId;
        final String userId;
        
        LoginIdParts(String tenantId, String userId) {
            this.tenantId = tenantId;
            this.userId = userId;
        }
    }
}
